module ActiveRecord #:nodoc: 
  module Acts #:nodoc:
    module MFD
      module ActsAsMultiFieldDate
        def self.included(base) # :nodoc:
          base.extend(ClassMethods)
        end

        module ClassMethods
          #<tt>:options</tt>:: Required options are: 
          #<tt>:date_field</tt>:: attribute name of full date  
          #<tt>:year_field</tt>:: attribute name of year (default: 'year')
          #<tt>:month_field</tt>:: attribute name of month (default: 'month')
          #<tt>:day_field</tt>:: attribute name of day (default: 'day')
          #
          #E.g., 
          #<tt>multi_field_date :date_field => 'birth_date', :year_field => 'birth_year', </tt>
          #<tt>   :month_field => 'birth_month', :day_field => 'birth_day </tt>
          def acts_as_multi_field_date(options = {})
            options = {:date_field => nil,
            :year_field => 'year', 
            :month_field => 'month',
            :day_field => 'day' }.merge(options)
            raise ArgumentError.new(":date_field must not be nil") if options[:date_field].nil?

            write_inheritable_attribute(:year_field, options[:year_field])
            write_inheritable_attribute(:month_field, options[:month_field])
            write_inheritable_attribute(:day_field, options[:day_field])
            write_inheritable_attribute(:date_field, options[:date_field])

            class_inheritable_reader :year_field
            class_inheritable_reader :month_field
            class_inheritable_reader :day_field
            class_inheritable_reader :date_field

            before_save :validate_and_update_date

            include ActiveRecord::Acts::MFD::Helpers
            include ActiveRecord::Acts::MFD::DateValidator
            include ActiveRecord::Acts::MFD::ActsAsMultiFieldDate::InstanceMethods
            extend  ActiveRecord::Acts::MFD::SingletonMethods
          end
        end #ClassMethods

        module InstanceMethods 
          def validate_and_update_date
            validate_month_and_day
            update_date
          end

          def validate_month_and_day
            if self.send(month_field) && self.send(day_field)
              dy = self.send(day_field)
              mo = self.send(month_field)
              yr = self.send(year_field)
              unless ActiveRecord::Acts::MFD::DateValidator.valid_date?(yr,mo,dy)
                msg = 'Invalid Date.  Most likely you have chosen a day that does not exist in the month you chose.'
                self.errors.add_to_base(msg)
              end
            end
          end

          def year
            return super if year_field == 'year'
            return self.send(year_field) if self.send(year_field)
          end

          def month 
            return super if month_field == 'month'
            return self.send(month_field) if self.send(month_field)
          end

          def day
            return super if day_field == 'day'
            return self.send(day_field) if self.send(day_field)
          end  

          #Updates the date_field if year, month, and day are present
          def update_date 
            yr = self.send(year_field) 
            mo = self.send(month_field)
            dy = self.send(day_field)
            if yr and mo and dy
              begin 
                self.send("#{date_field}=", Date.new(yr.to_i, mo.to_i, dy.to_i))
              rescue
                self.send("#{date_field}=", nil)
              end
            else
              self.send("#{date_field}=", nil)
            end
          end

          #Returns date text with missing elements (year, month, day) 
          #replaced by <tt>missing_char</tt> 
          #E.g., if only the year is present, the returned value would be something like:
          #2007-??-??
          def date_text(missing_char='?')
            x = missing_char
            return self.send(date_field).to_s if self.send(date_field)
            txt = ''
            txt << (self.send(year_field).nil? ? "#{x}#{x}#{x}#{x}" : self.send(year_field).to_s) << "-"
            txt << (self.send(month_field) ? zero_pad(self.send(month_field)) : "#{x}#{x}" ) << "-"
            txt << (self.send(day_field) ? zero_pad(self.send(day_field)) : "#{x}#{x}" ) 
            txt
          end

          #helper method to zero-pad an integer (0-9)

        end #InstanceMethods
      end #ActsAsMultiFieldDate
    end #MFD
  end #Acts
end #ActiveRecord
